/* 
 * Copyright 2010 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using Ngilead.Core.Replicator;
using log4net;
using Ngilead.Core;
using Ngilead.Core.Store;
using System.Collections.Generic;
using System.Reflection;
namespace Ngilead.Core.Replicator.Clone
{
    /// <summary>Property filter for clone operation</summary>
    /// <author>bruno.marchesson</author>
    public class ClonePropertyFilter : IPropertyFilter
    {
        /*----------------------------------------------------------------------------------------*/
        #region Attributes

        /// <summary> Logger channel</summary>
        private static ILog logger = LogManager.GetLogger(typeof(ClonePropertyFilter));

        #endregion
        /*----------------------------------------------------------------------------------------*/

        /*----------------------------------------------------------------------------------------*/
        #region Properties

        /// <summary>The associated persistence util</summary>
        public IPersistenceUtil PersistenceUtil { get; set; }

        /// <summary>Proxy store attribute</summary>
        public IProxyStore ProxyStore { get; set; }
        #endregion
        /*----------------------------------------------------------------------------------------*/

        /*----------------------------------------------------------------------------------------*/
        #region Constructor
        /// <summary>Base Constructor</summary>
        public ClonePropertyFilter(IPersistenceUtil persistenceUtil, IProxyStore proxyStore)
        {
            PersistenceUtil = persistenceUtil;
            ProxyStore = proxyStore;
        }

        /// <summary>Empty Constructor</summary>
        public ClonePropertyFilter()
        {
        }

        #endregion
        /*----------------------------------------------------------------------------------------*/

        /*----------------------------------------------------------------------------------------*/
        #region IPropertyFilter implementation

        /// <summary>
        /// Check if property should be populated
        /// </summary>
        /// <param name="propertyName">Property name</param>
        /// <param name="fromBean">Object from</param>
        /// <param name="toBean">Object to</param>
        /// <returns></returns>
        public bool ShouldPopulate(string propertyName,
                                   object fromBean,
                                   object toBean)
        {
            //  Precondition checking : do not clone the LightEntity specific fields
            if ((Constants.PROXY_INFORMATIONS.Equals(propertyName) == true) ||
                (Constants.INITIALIZATION_MAP.Equals(propertyName) == true))
            {
                return false;
            }

            //	Is the property lazy loaded ?
            Object fromValue = ReadPropertyValue(fromBean, toBean, propertyName);
            if (fromValue == null)
            {
                return true;
            }

            bool isPersistentDictionary = PersistenceUtil.IsPersistentDictionary(fromValue.GetType());
            bool isPersistentCollection = PersistenceUtil.IsPersistentCollection(fromValue.GetType());

            //	Lazy handling
            if (PersistenceUtil.IsInitialized(fromValue) == false)
            {
                //	Lazy property !
                if (logger.IsDebugEnabled)
                {
                    logger.Debug(String.Format("{0}.{1} : not initialized", toBean.GetType().ToString(), propertyName));
                }

                //	Get proxy informations
                Dictionary<string, Object> proxyInformations;
                if (isPersistentDictionary)
                {
                    proxyInformations = PersistenceUtil.SerializePersistentDictionary(fromValue);
                }
                else if (isPersistentCollection)
                {
                    proxyInformations = PersistenceUtil.SerializePersistentCollection(fromValue);
                }
                else
                {
                    proxyInformations = PersistenceUtil.SerializeEntityProxy(fromValue);
                }

                //	Add lazy property
                proxyInformations.Add(Constants.INITIALISED, false);

                //	Store proxy information
                ProxyStore.StoreProxyInformations(toBean, fromBean, propertyName, proxyInformations);

                //  Do not clone
                return false;
            }
            else if (isPersistentDictionary)
            {
                //	Persistent dictionary handling
                //
                Dictionary<string, Object> proxyInformations = PersistenceUtil.SerializePersistentDictionary(fromValue);
                ProxyStore.StoreProxyInformations(toBean, fromBean, propertyName, proxyInformations);
            }
            else if (isPersistentCollection)
            {
                //	Persistent collection handling
                //
                Dictionary<string, Object> proxyInformations = PersistenceUtil.SerializePersistentCollection(fromValue);
                ProxyStore.StoreProxyInformations(toBean, fromBean, propertyName, proxyInformations);
            }

            return true;
        }

        #endregion
        /*----------------------------------------------------------------------------------------*/

        /*----------------------------------------------------------------------------------------*/
        #region Internal methods
        /// <summary>
        /// Read a property value, even if it has a private getter
        /// Be carefull Fire the loading if the property is not initialized
        /// </summary>
        /// <param name="fromBean">the source bean</param>
        /// <param name="toBean">the target bean</param>/// 
        /// <param name="propertyName">the property name</param>
        /// <returns>the value of the property</returns>
        private Object ReadPropertyValue(Object fromBean, Object toBean, string propertyName)
        {

            //  Get property
            Type type = toBean.GetType();
            PropertyInfo property = type.GetProperty(propertyName);
            //  Get associated value
            return property.GetValue(fromBean, null);
        }

        #endregion
        /*----------------------------------------------------------------------------------------*/
    }
}